package com.jsn.android.audio2

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.observe
import com.jsn.android.audio2.databinding.ActivityWavBinding
import com.jsn.baselibx.ui.BaseActivity
import kotlinx.android.synthetic.main.activity_wav.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.io.*
import java.nio.ByteBuffer
import java.nio.ByteOrder


class WAVActivity : BaseActivity<ActivityWavBinding>() {

    val fileName = "1583905552833.pcm"

    var inputStream: InputStream? = null

    var inWritingFlow = MutableLiveData<Boolean>().apply { value = false }

    var size = -1

    override fun getContentView(): Int {
        return R.layout.activity_wav
    }

    override fun initUI() {
        bt.setOnClickListener {
            startConversionFlow()
        }

        inWritingFlow.observe(this) { inProgress ->
            bt.isEnabled = !inProgress
            bt.text=if(inProgress) "converting" else "start"
        }
    }

    private fun prepare(): Boolean {
        try {
            val open: InputStream = assets.open(fileName).also { inputStream = it }
            return inputStream != null
        } catch (e: Exception) {
            return false
        }

    }

    override fun initData() {

    }

    val job = SupervisorJob()

    val scope = CoroutineScope(Dispatchers.IO + job)

    fun startConversionFlow() {
        inWritingFlow.value = true
        val prepared = prepare()
        if (!prepared) {
            inWritingFlow.value = false
            return
        }
        //now we have a inputStream
        val file = File(cacheDir, "${System.currentTimeMillis()}.wav").apply {
            if (!exists()) {
                val createNewFile = createNewFile()
                if (!createNewFile) {
                    inWritingFlow.value = false
                    return
                }
            }
        }
        //we hava file .write it
        scope.launch {
            convert(file)
        }
    }

    var fileOS: FileOutputStream? = null

    var dataOS: DataOutputStream? = null

    var inputT: DataInputStream? = null

    fun convert(file: File) {
        try {
            val temp = File(cacheDir, "temp${System.currentTimeMillis()}").apply {
                if (!exists()) createNewFile()
            }
            inputStream?.run { writeBytesToFile(this, temp) }
            val length = temp.length()

            val rawData = ByteArray(temp.length().toInt())
            var input: DataInputStream? = null
            input = DataInputStream(FileInputStream(temp)).also { inputT = it }
            input.read(rawData);


            val fileOutputStream = FileOutputStream(file).also { fileOS = it }
            val output = DataOutputStream(fileOutputStream).also { dataOS = it }

            writeString(output, "RIFF") // chunk id

            writeInt(output, 36 + length.toInt()) // chunk size

            writeString(output, "WAVE") // format

            writeString(output, "fmt ") // subchunk 1 id

            writeInt(output, 16) // subchunk 1 size

            writeShort(output, 1.toShort()) // audio format (1 = PCM)

            writeShort(output, 1.toShort()) // number of channels

            writeInt(output, 44100) // sample rate

            writeInt(output, 44100 * 2) // byte rate

            writeShort(output, 2.toShort()) // block align

            writeShort(output, 16.toShort()) // bits per sample

            writeString(output, "data") // subchunk 2 id

            writeInt(output, length.toInt()) // subchunk 2 size

            val shorts = ShortArray((length / 2).toInt())
            ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts)
            val bytes: ByteBuffer = ByteBuffer.allocate(shorts.size * 2)
            for (s in shorts) {
                bytes.putShort(s)
            }

            output.write(fullyReadFileToBytes(temp))
            runOnUiThread {
                toast("${file.name} ok")
            }

        } finally {

            fileOS?.close()
            dataOS?.close()
            inputT?.close()
            inWritingFlow.postValue(false)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }

    fun fullyReadFileToBytes(f: File): ByteArray {
        val length = f.length().toInt()
        size=length
        val bytes = ByteArray(size)
        val tempBuff = ByteArray(size)
        val fis = FileInputStream(f)

        val read = fis.read(bytes, 0, size)
        if (read < size) {
            var remain = size - read
            while (remain > 0) {
                val read1 = fis.read(tempBuff, 0, remain)
                System.arraycopy(tempBuff, 0, bytes, size - remain, read1)
                remain -= read1
            }
        }
        return bytes
    }
}


@Throws(IOException::class)
private fun writeInt(output: DataOutputStream, value: Int) {
    //little endian 1234 4321
    output.write(value shr 0)
    output.write(value shr 8)
    output.write(value shr 16)
    output.write(value shr 24)
}

@Throws(IOException::class)
private fun writeShort(output: DataOutputStream, value: Short) {
    output.write(value.toInt() shr 0)
    output.write(value.toInt() shr 8)
}

@Throws(IOException::class)
private fun writeString(output: DataOutputStream, value: String) {
    for (i in 0 until value.length) {
        output.write(value[i].toInt())
    }
}

@Throws(IOException::class)
fun writeBytesToFile(`is`: InputStream, file: File?) {
    var fos: FileOutputStream? = null
    try {
        val data = ByteArray(2048)
        var nbread = 0
        fos = FileOutputStream(file)
        while (`is`.read(data).also { nbread = it } > -1) {
            fos.write(data, 0, nbread)
        }
    } catch (ex: Exception) {
        //logger.error("Exception", ex)
        throw ex
    } finally {
        fos?.close()
    }
}
